package com.leo.foodmaster.admin.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.leo.foodmaster.admin.system.dto.UserDTO;
import com.leo.foodmaster.admin.system.entity.*;
import com.leo.foodmaster.admin.system.mapper.UserMapper;
import com.leo.foodmaster.admin.system.service.IOrganService;
import com.leo.foodmaster.admin.system.service.IUserRoleService;
import com.leo.foodmaster.admin.system.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leo.foodmaster.commons.constants.AuthConstants;
import com.leo.foodmaster.commons.constants.Constants;
import com.leo.foodmaster.commons.entity.Result;
import com.leo.foodmaster.commons.enums.MsgEnum;
import com.leo.foodmaster.commons.enums.OrganTypeEnum;
import com.leo.foodmaster.commons.enums.StatusEnum;
import com.leo.foodmaster.commons.exception.AuthenticationFailedException;
import com.leo.foodmaster.commons.util.IpUtils;
import com.leo.foodmaster.commons.util.ServletUtils;
import com.leo.foodmaster.commons.util.TokenUtils;
import com.leo.foodmaster.commons.util.WebUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Leo
 * @since 2021-06-17
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, SysUser> implements IUserService {
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private AuthenticationManager authenticationManager;
    @Resource
    private TokenUtils tokenUtils;
    @Resource
    private IOrganService organService;
    @Resource
    private IUserRoleService userRoleService;
    @Resource
    private ISysLoginLogServiceImpl sysLoginLogServiceImpl;

    /**
     * 用户登录
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param captchaId 验证码唯一标识
     * @return token
     */
    @Override
    public String login(String username, String password, String code, String captchaId) {

        // 一、判断验证码是否正确
        String captcha = redisTemplate.opsForValue().get(AuthConstants.CAPTCHA_CODE_KEY + captchaId);
        redisTemplate.delete(AuthConstants.CAPTCHA_CODE_KEY + captchaId);
        if (StrUtil.isBlank(captcha)) {
            throw new AuthenticationFailedException("验证码过期！");
        }
        if (!code.equalsIgnoreCase(captcha)) {
            throw new AuthenticationFailedException("验证码错误！");
        }
        // 二、用户认证
        Authentication authentication;
        try {
            authentication = authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (AuthenticationException e) {
            if (e instanceof BadCredentialsException) {
                String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
                sysLoginLogServiceImpl.asyncSaveLoginLog(ipAddr, username, Constants.LOGIN_FAIL, "账号：" + username + " 的密码错误");
                throw new AuthenticationFailedException("对不起，您的密码错误！");
            }else {
                throw e;
            }
        }
        // 三、保存认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 三、生成token
        assert authentication != null;
        User user = (User) authentication.getPrincipal();
        Organ organ = organService.getById(user.getOrganId());
        String roles = user.getAuthorities().stream().map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(StrUtil.COMMA));
        UserBean userBean = new UserBean();
        BeanUtil.copyProperties(user, userBean);
        userBean.setOrganName(organ.getName());
        userBean.setOrganType(organ.getType());
        String foodSupplyTypes = organ.getFoodSupplyTypes();
        if (StrUtil.isNotBlank(foodSupplyTypes)) {
            userBean.setFoodSupplyType(Arrays.asList(foodSupplyTypes.split(StrUtil.COMMA).clone()));
        }
        if (StrUtil.isNotBlank(roles)) {
            userBean.setRoles(roles);
        }else {
            userBean.setRoles("ROLE_ANONYMOUS");
        }
        // 登录成功，添加登录日志
        String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
        sysLoginLogServiceImpl.asyncSaveLoginLog(ipAddr, username, Constants.LOGIN_SUCCESS, "账号：" + username + " 登录成功");
        return tokenUtils.createToken(userBean);
    }

    /**
     * 分页条件查询用户列表
     */
    @Override
    public IPage<SysUser> getUserPageList(Page<SysUser> page, Long organId, Long deptId, String account, String phone,
                                       Integer status, String beginTime, String endTime) {
        // 如果机构Id为空，且用户所在公司不是平台，则查询当前用户所在机构的所有用户
        String organType = WebUtils.getUser().getOrganType();
        if (!OrganTypeEnum.PLATFORM.getCode().equals(organType) && BeanUtil.isEmpty(organId)) {
            organId = WebUtils.getUser().getOrganId();
        }

        return baseMapper.getUserPageList(page, account, phone, status, beginTime, endTime, organId, deptId);
    }

    /**
     * 新增用户
     * @param userDto 用户信息
     * @return Result<Void>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> addUser(UserDTO userDto) {
        String account = userDto.getAccount();
        String phone = userDto.getPhone();
        String email = userDto.getEmail();
        List<Long> roleIds = userDto.getRoleIds();
        // 从用户表中查询账户，用户账户不能重复
        int count;
        count = this.count(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getAccount, account));
        if (count != 0) {
            return Result.fail(MsgEnum.USER_ACCOUNT_EXISTED);
        }
        // 从用户表中查询手机号，不能重复
        if (StrUtil.isNotBlank(phone)) {
            count = this.count(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getPhone, phone));
            if (count != 0) {
                return Result.fail(MsgEnum.USER_PHONE_EXISTED);
            }
        }
        // 从用户表中查询邮箱地址，不能重复
        if (StrUtil.isNotBlank(email)) {
            count = this.count(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getEmail, email));
            if (count != 0) {
                return Result.fail(MsgEnum.USER_EMAIL_EXISTED);
            }
        }
        // 新增用户
        SysUser user = new SysUser();
        BeanUtil.copyProperties(userDto, user);
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        user.setPassword(encoder.encode(userDto.getPassword()));
        this.save(user);

        // 如果用户角色Id列表不为空，则添加用户——角色关联表
        addUserRole(roleIds, user.getId());
        return Result.success(MsgEnum.REQUEST_SUCCESS);
    }

    /**
     * 修改用户
     * @param userDto 用户信息
     * @return Result<Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> updateUser(UserDTO userDto) {
        // 修改用户信息
        SysUser sysUser = new SysUser();
        BeanUtil.copyProperties(userDto, sysUser);
        this.updateById(sysUser);

        // 修改用户角色信息
        List<Long> roleIds = userDto.getRoleIds();
        // 删除原来的用户角色信息
        userRoleService.remove(Wrappers.lambdaUpdate(UserRole.class)
                .eq(UserRole::getUserId, sysUser.getId()));
        // 新增角色信息
        addUserRole(roleIds, sysUser.getId());

        return Result.success();
    }

    /**
     * 删除用户
     * @param userIds 用户Ids
     * @return Result<Object>
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result<Object> deleteUser(String userIds) {
        List<String> userIdList = Arrays.stream(userIds.split(StrUtil.COMMA)).collect(Collectors.toList());

        // 删除用户信息
        this.removeByIds(userIdList);

        // 删除用户角色信息
        userIdList.forEach(userId -> userRoleService.remove(Wrappers.lambdaUpdate(UserRole.class)
                                    .eq(UserRole::getUserId, userId)));
        return Result.success();
    }

    /**
     * 修改用户状态
     * @param sysUser 用户状态信息
     * @return Result<Object>
     */
    @Override
    public Result<Object> updateStatus(SysUser sysUser) {
        // 如果该用户对应的公司状态为禁用，则不能修改用户状态
        String organStatus = baseMapper.getOrganStatus(sysUser.getId());
        if (StrUtil.isNotBlank(organStatus) && StatusEnum.DISABLED.getCode().equals(organStatus)) {
            return Result.fail("该用户对应的公司被禁用，该用户不能被启用！");
        }
        updateById(sysUser);
        return Result.success();
    }

    /**
     * 新增用户角色信息
     * @param roleIds 角色Ids
     * @param userId 用户Id
     */
    private void addUserRole(List<Long> roleIds, Long userId) {
        if (!roleIds.isEmpty()) {
            List<UserRole> userRoleList = new ArrayList<>();
            for (Long roleId : roleIds) {
                UserRole userRole = new UserRole(userId, roleId);
                userRoleList.add(userRole);
            }
            userRoleService.saveBatch(userRoleList);
        }
    }

}
